
#include "../inc/main.h"

// Static strings for types
#define STR_ID_INTEGER		0
#define STR_ID_UNSIGNED32	1
#define STR_ID_COUNTER64	2
#define STR_ID_OCTETSTRING	3
#define STR_ID_BITS			4
#define STR_ID_OBJECTID		5
#define STR_ID_SEQUENCE		6
#define STR_ID_IPADDRESS	7
#define STR_ID_COUNTER32	8
#define STR_ID_GAUGE32		9
#define STR_ID_TIMETICKS	10
#define STR_ID_OPAQUE		11
#define STR_ID_UNKNOWN		12


static char console_string[SIZE_STRING];

// ASN type strings
const char* asn_types_str[] = {	"Integer", "Unsigned32", "Counter64", "OctetString",
									 "Bits", "ObjectID", "Sequence", "IPAddress",
									 "Counter32", "Gauge32", "Timeticks", "Opaque", "N/C" };

// This function desallocates memory for the specified item
void free_oid_item(struct oid_item* item)
{
    // Name
    if(&(item->name)) SnmpUtilMemFree(&(item->name));

    // Type is a pointer to a constant string,
    // hence there is no need to free it

    // Value
    //if(&(item->value)) free(&(item->name));

}

// Convert an RFC1157 binding to a string structure
// containing the name, type and value of the variable
// The function free_oid_item must be called once the item has been used
void convert_oid_item(RFC1157VarBind* binding, struct oid_item* item)
{
	//char tmp_value[TMP_VALUE_MAXSIZE];
	unsigned int value_len;
	unsigned int ticks, days, hours, mins, secs;

    // Get the name
    // This function allocates memory to item.name
    // Therefore, it needs to be freed after usage
    SnmpMgrOidToStr(&(binding->name), &(item->name));

    // Get the type (do not allocate new memory space, this is just a pointer
		// to a static string)
		switch(binding->value.asnType)
		{
			case ASN_INTEGER:			item->type = (char*) asn_types_str[STR_ID_INTEGER]; break;
			case ASN_UNSIGNED32:		item->type = (char*) asn_types_str[STR_ID_UNSIGNED32]; break;
			case ASN_COUNTER64:			item->type = (char*) asn_types_str[STR_ID_COUNTER64]; break;
			case ASN_OCTETSTRING:		item->type = (char*) asn_types_str[STR_ID_OCTETSTRING]; break;
			case ASN_BITS:				item->type = (char*) asn_types_str[STR_ID_BITS]; break;
			case ASN_OBJECTIDENTIFIER:	item->type = (char*) asn_types_str[STR_ID_OBJECTID]; break;
			case ASN_SEQUENCE:			item->type = (char*) asn_types_str[STR_ID_SEQUENCE]; break;
			case ASN_IPADDRESS:			item->type = (char*) asn_types_str[STR_ID_IPADDRESS]; break;
			case ASN_COUNTER32:			item->type = (char*) asn_types_str[STR_ID_COUNTER32]; break;
			case ASN_GAUGE32:			item->type = (char*) asn_types_str[STR_ID_GAUGE32]; break;
			case ASN_TIMETICKS:			item->type = (char*) asn_types_str[STR_ID_TIMETICKS]; break;
			case ASN_OPAQUE:			item->type = (char*) asn_types_str[STR_ID_OPAQUE]; break;
			default:					item->type = (char*) asn_types_str[STR_ID_UNKNOWN]; break;
		}

        // Reset string
        for(value_len = VALUE_MAXSIZE; value_len; value_len--)
            item->value[value_len] = 0;

		// Get the value
		switch(binding->value.asnType)
		{
			// Decimal numbers
			case ASN_INTEGER:
			case ASN_UNSIGNED32:
			case ASN_COUNTER32:
			case ASN_COUNTER64:
			case ASN_GAUGE32:
			case ASN_OBJECTIDENTIFIER:
				value_len = sprintf(item->value,"%d",binding->value.asnValue);
			break;

			// Hex numbers
			case ASN_BITS:
			case ASN_SEQUENCE:
			case ASN_OPAQUE:
				value_len = sprintf(item->value,"0x%X",binding->value.asnValue);
			break;

            // Strings
            case ASN_OCTETSTRING:
                memcpy(item->value, binding->value.asnValue.string.stream, binding->value.asnValue.string.length);
            break;

            // Special formats
			case ASN_IPADDRESS:
                //value_len = sprintf(item->value,"0x%X",binding->value.asnValue);
                //memcpy(&ticks, &binding->value.asnValue.stream[0], 4);
                value_len = sprintf(item->value,"%03d.%03d.%03d.%03d",
                    binding->value.asnValue.string.stream[0],
                    binding->value.asnValue.string.stream[1],
                    binding->value.asnValue.string.stream[2],
                    binding->value.asnValue.string.stream[3]);
                    /*(ticks&0xFF000000)>>24,
                    (ticks&0x00FF0000)>>16,
                    (ticks&0x0000FF00)>>8,
                    (ticks&0x000000FF));*/
			break;

			case ASN_TIMETICKS:
                memcpy(&ticks, &binding->value.asnValue, 4);
                days = ticks/8640000;
                hours = (ticks- days*8640000)/360000;
                mins = (ticks - (days*8640000 + hours*360000))/6000;
                secs = (ticks - (days*8640000 + hours*360000 + mins*6000))/100;
                value_len = sprintf(item->value,"%dd %dh %dm %ds",days, hours, mins, secs);
			break;

			default:
				value_len = sprintf(item->value,"N/C");
			break;
		}
 }

int constructVB(RFC1157VarBindList * variableBindings, char * oid_s,HWND hwnd){
	AsnObjectIdentifier reqObject;
	RFC1157VarBind * tmpVb;


	// Convert the string representation to an internal representation.
	if (!SnmpMgrStrToOid(oid_s, &reqObject))
	{
		sprintf(console_string,"Error: Invalid oid, %s, specified.", oid_s);
		cPrintf (hwnd, console_string);
		return 1;
	}
	else
	{
		// Since sucessfull, add to the variable bindings list.
		(variableBindings->len)++;
		if ((tmpVb = (RFC1157VarBind *)SnmpUtilMemReAlloc(
			variableBindings->list, sizeof(RFC1157VarBind) *
			variableBindings->len)) == NULL)
		{
			sprintf(console_string,"Error: Error allocating oid, %s.",oid_s);
			cPrintf (hwnd, console_string);
			SnmpUtilOidFree(&reqObject);
			(variableBindings->len)--;
			SnmpUtilVarBindListFree(variableBindings);

			return 1;
		}
		variableBindings->list = tmpVb;

		variableBindings->list[variableBindings->len - 1].name =
			reqObject; // NOTE!  structure copy
		variableBindings->list[variableBindings->len - 1].value.asnType =
			ASN_NULL;
	}
	return 0;
}


// Establish a SNMP session to communicate with the remote agent.  The
// community, communications timeout, and communications retry count
// for the session are also required.
LPSNMP_MGR_SESSION SNMPOpenSession(LPSTR agent, LPSTR community, int timeout, int retries,HWND hwnd){


	LPSNMP_MGR_SESSION session;

	if ((session = SnmpMgrOpen(agent, community, timeout, retries)) == NULL)
	{
		sprintf(console_string,"error on SnmpMgrOpen %d", GetLastError());
		cPrintf (hwnd, console_string);
		SnmpUtilMemFree(agent);
		SnmpUtilMemFree(community);

		return NULL;
	}

	return session;
}
//


int SNMPWalk(LPSNMP_MGR_SESSION session, RFC1157VarBindList * variableBindings, HWND hwnd)
{

	AsnObjectIdentifier root;
	AsnObjectIdentifier tempOid;
	AsnInteger errorStatus;
	AsnInteger errorIndex;
    struct oid_item item;

	if (!SnmpUtilOidCpy(&root, &variableBindings->list[0].name))
	{
		sprintf(console_string,"error on SnmpUtilOidCpy");
		cPrintf (hwnd, console_string);
		//SnmpUtilMemFree(agent);
		//SnmpUtilMemFree(community);
		//SnmpUtilVarBindListFree(variableBindings);
		return 1;
	}

	BYTE requestType = ASN_RFC1157_GETNEXTREQUEST;


	while(1)
	{
		if (!SnmpMgrRequest(session, requestType, variableBindings,
			&errorStatus, &errorIndex))
		{
			// The API is indicating an error.
			sprintf(console_string,"error on SnmpMgrRequest %d", GetLastError());
            cPrintf (hwnd, console_string);

			break;
		}
		else
		{
			// The API succeeded, errors may be indicated from the remote
			// agent.


			// Test for end of subtree or end of MIB.

			if (errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME ||
				SnmpUtilOidNCmp(&variableBindings->list[0].name,
				&root, root.idLength))
			{
				sprintf(console_string,"End of MIB subtree.");
                cPrintf (hwnd, console_string);
                cPrintf (hwnd, ""); //printf("\n");
				break;
			}


			// Test for general error conditions or sucesss.

			if (errorStatus > 0)
			{
				sprintf(console_string,"Error: errorStatus=%d, errorIndex=%d",
					errorStatus, errorIndex);
                cPrintf (hwnd, console_string);

				break;
			}
			else
			{
				// Display resulting variable binding for this iteration.

				char *string = NULL;
				if (SnmpMgrOidToStr(&variableBindings->list[0].name, &string))
				{
					sprintf(console_string,"Variable = %s", string);
					cPrintf (hwnd, console_string);
					if (string) SnmpUtilMemFree(string);
				}
				sprintf(console_string,"Value    = ");



				//SnmpUtilPrintAsnAny(&variableBindings->list[0].value);

				// Fill the table
                convert_oid_item(&variableBindings->list[0], &item);
                updateListView (hwnd, &item);
                strcat(console_string,item.value);
                cPrintf (hwnd, console_string);
                free_oid_item(&item);

				//cPrintf (hwnd, ""); //printf("\n");
			}
		} // end if()


		// Prepare for the next iteration.  Make sure returned oid is
		// preserved and the returned value is freed.

		if (!SnmpUtilOidCpy(&tempOid, &variableBindings->list[0].name))
		{
			sprintf(console_string,"error on SnmpUtilOidCpy");
			cPrintf (hwnd, console_string);
			SnmpUtilOidFree(&root);
			//SnmpUtilMemFree(agent);	// this is freed when the function returns
			//SnmpUtilMemFree(community);	// this is freed when the function returns
			SnmpUtilVarBindListFree(variableBindings);
			return 1;
		}

		SnmpUtilVarBindFree(&variableBindings->list[0]);

		if (!SnmpUtilOidCpy(&variableBindings->list[0].name, &tempOid))
		{
			sprintf(console_string,"error on SnmpUtilOidCpy");
			cPrintf (hwnd, console_string);
			SnmpUtilOidFree(&tempOid);
			SnmpUtilOidFree(&root);
			//SnmpUtilMemFree(agent);	// this is freed when the function returns
			//SnmpUtilMemFree(community);	// this is freed when the function returns
			SnmpUtilVarBindListFree(variableBindings);
			return 1;
		}
		variableBindings->list[0].value.asnType = ASN_NULL;

		SnmpUtilOidFree(&tempOid);

	} // end while()


	// Free the variable bindings that have been allocated.
	//SnmpUtilVarBindListFree(&variableBindings);	// this is freed when the function returns

	// Free other allocated resources
	SnmpUtilOidFree(&root);
	//SnmpUtilMemFree(agent);	// this is freed when the function returns
	//SnmpUtilMemFree(community);	// this is freed when the function returns
	return 0;
}

//


// Walk is a common term used to indicate that all MIB variables
// under a given OID are to be traversed and displayed.  This is
// a more complex operation requiring tests and looping in addition
// to the steps for get/getnext above.
void SNMPRequestWalk(char *agent_c, char *community_c, char *oid_s, HWND hwnd)
{

	//convert agent and community to LPSTR
	LPSTR agent = NULL;
	LPSTR community = NULL;
	/*/if (StringtoLPSTR(agent_c, &agent)){
		return;
	}
	if (StringtoLPSTR(community_c, &community)){
		return;
	}*/

    sprintf(console_string,"Request %s %s %s", agent_c, community_c, oid_s);
    cPrintf (hwnd, console_string);
	// construct variableBindings
	RFC1157VarBindList vb;
	vb.len = 0;
	vb.list = NULL;
	RFC1157VarBindList * variableBindings = &vb;
	if (constructVB(variableBindings, oid_s,hwnd)){
		return;
	}


	//open session
	LPSNMP_MGR_SESSION session;
	if ((session = SNMPOpenSession(agent_c, community_c, TIMEOUT, RETRIES,hwnd)) == NULL){
		return;
	}

	// perform the SNMP request
	SNMPWalk(session, variableBindings, hwnd);

	// cleanup
	// Free the variable bindings that have been allocated.
	SnmpUtilVarBindListFree(variableBindings);
	SnmpUtilMemFree(agent);
	SnmpUtilMemFree(community);

	//close session
	if (!SnmpMgrClose(session)){
		sprintf("error on SnmpMgrClose %d", GetLastError());
		cPrintf (hwnd, console_string);
	}
}


//


// performs the actual GET/GETNEXT request
void SNMPGet(LPSNMP_MGR_SESSION session,
	RFC1157VarBindList * variableBindings,
	BYTE requestType,HWND hwnd)
{
	AsnInteger errorStatus;
	AsnInteger errorIndex;
    struct oid_item item;
	// Request that the API carry out the desired operation.

	if (!SnmpMgrRequest(session, requestType, variableBindings,
		&errorStatus, &errorIndex))
	{
		// The API is indicating an error.

		sprintf(console_string,"error on SnmpMgrRequest %d", GetLastError());
		cPrintf (hwnd, console_string);
	}
	else
	{
		// The API succeeded, errors may be indicated from the remote
		// agent.

		if (errorStatus > 0)
		{
			sprintf(console_string,"Error: errorStatus=%d, errorIndex=%d",errorStatus, errorIndex);
			cPrintf (hwnd, console_string);
		}
		else
		{
			// Display the resulting variable bindings.

			UINT i;

			for(i=0; i < variableBindings->len; i++)
			{
				char *string = NULL;

				if (SnmpMgrOidToStr(&variableBindings->list[i].name, &string))
				{
					sprintf(console_string,"Variable = %s", string);
					cPrintf (hwnd, console_string);
					if (string) SnmpUtilMemFree(string);
				}
				sprintf(console_string,"Value    = ");
                convert_oid_item(&variableBindings->list[0], &item);
                strcat(console_string,item.value);
                cPrintf (hwnd, console_string);
                free_oid_item(&item);
				cPrintf (hwnd, "");//printf("\n");
			} // end for()
		}
	}
}

//


//Get and GetNext are relatively simple operations to perform.
//Simply initiate the request and process the result and/or
//possible error conditions.
void SNMPRequestGet(char *agent_c, char *community_c, char *oid_s,HWND hwnd){

	//convert agent and community to LPSTR
	LPSTR agent = NULL;
	LPSTR community = NULL;

	/*if (StringtoLPSTR(agent_c, &agent)){
		return;
	}
	if (StringtoLPSTR(community_c, &community)){
		return;
	}*/

	// construct variableBindings
	RFC1157VarBindList vb;
	vb.len = 0;
	vb.list = NULL;
	RFC1157VarBindList * variableBindings = &vb;
	if (constructVB(variableBindings, oid_s,hwnd)){
		return;
	}

	//open session
	LPSNMP_MGR_SESSION session;
	if ((session = SNMPOpenSession(agent_c, community_c, TIMEOUT, RETRIES,hwnd)) == NULL){
		return;
	}

	// perform the SNMP GET operation
	SNMPGet(session, variableBindings, ASN_RFC1157_GETREQUEST,hwnd);

	// cleanup
	// Free the variable bindings that have been allocated.
	SnmpUtilVarBindListFree(variableBindings);
	SnmpUtilMemFree(agent);
	SnmpUtilMemFree(community);

	//close session
	if (!SnmpMgrClose(session))
	{
		sprintf(console_string,"error on SnmpMgrClose %d", GetLastError());
		cPrintf (hwnd, console_string);
	}

}


//Get and GetNext are relatively simple operations to perform.
//Simply initiate the request and process the result and/or
//possible error conditions.
void SNMPRequestGetNext(char *agent_c, char *community_c, char *oid_s,HWND hwnd){

	//convert agent and community to LPSTR
	LPSTR agent = NULL;
	LPSTR community = NULL;

	/*if (StringtoLPSTR(agent_c, &agent)){
		return;
	}
	if (StringtoLPSTR(community_c, &community)){
		return;
	}*/

	// construct variableBindings
	RFC1157VarBindList vb;
	vb.len = 0;
	vb.list = NULL;
	RFC1157VarBindList * variableBindings = &vb;
	if (constructVB(variableBindings, oid_s,hwnd)){
		return;
	}

	//open session
	LPSNMP_MGR_SESSION session;
	if ((session = SNMPOpenSession(agent_c, community_c, TIMEOUT, RETRIES,hwnd)) == NULL){
		return;
	}

	// perform the SNMP GET operation
	SNMPGet(session, variableBindings, ASN_RFC1157_GETREQUEST,hwnd);

	// cleanup
	// Free the variable bindings that have been allocated.
	SnmpUtilVarBindListFree(variableBindings);
	SnmpUtilMemFree(agent);
	SnmpUtilMemFree(community);

	//close session
	if (!SnmpMgrClose(session))
	{
		sprintf(console_string,"error on SnmpMgrClose %d", GetLastError());
		cPrintf (hwnd, console_string);
	}
}




